features: options.flag_features.as_slice(),
no_default_features: options.flag_no_default_features,
spec: options.flag_package.as_ref().map(|s| s.as_slice()),
- lib_only: false
+ lib_only: false,
+ exec_engine: None,
},
};
features: options.flag_features.as_slice(),
no_default_features: options.flag_no_default_features,
spec: options.flag_package.as_ref().map(|s| s.as_slice()),
- lib_only: options.flag_lib
+ lib_only: options.flag_lib,
+ exec_engine: None,
};
ops::compile(&root, &mut opts).map(|_| None).map_err(|err| {
features: options.flag_features.as_slice(),
no_default_features: options.flag_no_default_features,
spec: options.flag_package.as_ref().map(|s| s.as_slice()),
- lib_only: false
+ lib_only: false,
+ exec_engine: None,
},
};
features: options.flag_features.as_slice(),
no_default_features: options.flag_no_default_features,
spec: None,
- lib_only: false
+ lib_only: false,
+ exec_engine: None,
};
let (target_kind, name) = match (options.flag_bin, options.flag_example) {
features: options.flag_features.as_slice(),
no_default_features: options.flag_no_default_features,
spec: options.flag_package.as_ref().map(|s| s.as_slice()),
- lib_only: false
+ lib_only: false,
+ exec_engine: None,
},
};
use std::os;
use std::collections::HashMap;
use std::default::Default;
+use std::sync::Arc;
use core::registry::PackageRegistry;
use core::{MultiShell, Source, SourceId, PackageSet, Package, Target, PackageId};
use core::resolver::Method;
-use ops::{mod, BuildOutput};
+use ops::{mod, BuildOutput, ExecEngine};
use sources::{PathSource};
use util::config::{Config, ConfigValue};
use util::{CargoResult, config, internal, human, ChainError, profile};
pub features: &'a [String],
pub no_default_features: bool,
pub spec: Option<&'a str>,
- pub lib_only: bool
+ pub lib_only: bool,
+ pub exec_engine: Option<Arc<Box<ExecEngine>>>,
}
pub fn compile(manifest_path: &Path,
-> CargoResult<ops::Compilation> {
let CompileOptions { env, ref mut shell, jobs, target, spec,
dev_deps, features, no_default_features,
- lib_only } = *options;
+ lib_only, ref mut exec_engine } = *options;
+
let target = target.map(|s| s.to_string());
let features = features.iter().flat_map(|s| {
s.as_slice().split(' ')
try!(ops::compile_targets(env.as_slice(), targets.as_slice(), to_build,
&PackageSet::new(packages.as_slice()),
&resolve_with_overrides, &sources,
- &config, lib_overrides))
+ &config, lib_overrides, exec_engine.clone()))
};
return Ok(ret);
no_default_features: false,
spec: None,
lib_only: false,
+ exec_engine: None,
}));
Ok(())
use std::os;
-use ops;
+use ops::{mod, ExecEngine};
use util::{CargoResult, human, process, ProcessError, ChainError};
use core::manifest::TargetKind;
use core::source::Source;
Some(path) => path,
None => exe,
};
- let process = try!(compile.process(exe, &root))
+ let process = try!(try!(compile.target_process(exe, &root))
+ .into_process_builder())
.args(args)
.cwd(try!(os::getcwd()));
use core::{PackageId, Package};
use util::{mod, CargoResult};
+use super::{CommandType, CommandPrototype};
+
/// A structure returning the result of a compilation.
pub struct Compilation {
/// All libraries which were built for a package.
}
}
+ /// See `process`.
+ pub fn rustc_process(&self, pkg: &Package) -> CargoResult<CommandPrototype> {
+ self.process(CommandType::Rustc, pkg)
+ }
+
+ /// See `process`.
+ pub fn rustdoc_process(&self, pkg: &Package) -> CargoResult<CommandPrototype> {
+ self.process(CommandType::Rustdoc, pkg)
+ }
+
+ /// See `process`.
+ pub fn target_process<T: ToCStr>(&self, cmd: T, pkg: &Package)
+ -> CargoResult<CommandPrototype> {
+ self.process(CommandType::Target(cmd.to_c_str()), pkg)
+ }
+
+ /// See `process`.
+ pub fn host_process<T: ToCStr>(&self, cmd: T, pkg: &Package)
+ -> CargoResult<CommandPrototype> {
+ self.process(CommandType::Host(cmd.to_c_str()), pkg)
+ }
+
/// Prepares a new process with an appropriate environment to run against
/// the artifacts produced by the build process.
///
/// The package argument is also used to configure environment variables as
/// well as the working directory of the child process.
- pub fn process<T: ToCStr>(&self, cmd: T, pkg: &Package)
- -> CargoResult<util::ProcessBuilder> {
+ pub fn process(&self, cmd: CommandType, pkg: &Package)
+ -> CargoResult<CommandPrototype> {
let mut search_path = DynamicLibrary::search_path();
for dir in self.native_dirs.values() {
search_path.push(dir.clone());
search_path.push(self.deps_output.clone());
let search_path = try!(util::join_paths(search_path.as_slice(),
DynamicLibrary::envvar()));
- let mut cmd = try!(util::process(cmd)).env(
+ let mut cmd = try!(CommandPrototype::new(cmd)).env(
DynamicLibrary::envvar(), Some(search_path.as_slice()));
for (k, v) in self.extra_env.iter() {
cmd = cmd.env(k.as_slice(), v.as_ref().map(|s| s.as_slice()));
use super::TargetConfig;
use super::layout::{Layout, LayoutProxy};
use super::custom_build::BuildState;
+use super::{ProcessEngine, ExecEngine};
#[deriving(Show, Copy)]
pub enum Platform {
pub sources: &'a SourceMap<'b>,
pub compilation: Compilation,
pub build_state: Arc<BuildState>,
+ pub exec_engine: Arc<Box<ExecEngine>>,
env: &'a str,
host: Layout,
compilation: Compilation::new(root_pkg),
build_state: Arc::new(BuildState::new(build_config.clone(), deps)),
build_config: build_config,
+ exec_engine: Arc::new(box ProcessEngine as Box<ExecEngine>),
})
}
use super::job::Work;
use super::{fingerprint, process, Kind, Context, Platform};
+use super::CommandType;
use util::Freshness;
/// Contains the parsed output of a custom build script.
// Start preparing the process to execute, starting out with some
// environment variables.
let profile = target.get_profile();
- let mut p = try!(super::process(to_exec, pkg, target, cx))
+ let mut p = try!(super::process(CommandType::Host(to_exec.to_c_str()), pkg, target, cx))
.env("OUT_DIR", Some(&build_output))
.env("CARGO_MANIFEST_DIR", Some(pkg.get_manifest_path()
.dir_path()
try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Target).build(pkg), USER_RWX));
try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Host).build(pkg), USER_RWX));
+ let exec_engine = cx.exec_engine.clone();
+
// Prepare the unit of "dirty work" which will actually run the custom build
// command.
//
// And now finally, run the build command itself!
desc_tx.send_opt(p.to_string()).ok();
- let output = try!(p.exec_with_output().map_err(|mut e| {
+ let output = try!(exec_engine.exec_with_output(p).map_err(|mut e| {
e.desc = format!("failed to run custom build command for `{}`\n{}",
pkg_name, e.desc);
Human(e)
--- /dev/null
+use std::collections::HashMap;
+use std::c_str::CString;
+use std::io::process::ProcessOutput;
+use std::fmt::{mod, Show, Formatter};
+
+use util::{mod, CargoResult, ProcessError, ProcessBuilder};
+
+/// Trait for objects that can execute commands.
+pub trait ExecEngine: Send + Sync {
+ fn exec(&self, CommandPrototype) -> Result<(), ProcessError>;
+ fn exec_with_output(&self, CommandPrototype) -> Result<ProcessOutput, ProcessError>;
+}
+
+/// Default implementation of `ExecEngine`.
+#[deriving(Copy)]
+pub struct ProcessEngine;
+
+impl ExecEngine for ProcessEngine {
+ fn exec(&self, command: CommandPrototype) -> Result<(), ProcessError> {
+ command.into_process_builder().unwrap().exec()
+ }
+
+ fn exec_with_output(&self, command: CommandPrototype)
+ -> Result<ProcessOutput, ProcessError> {
+ command.into_process_builder().unwrap().exec_with_output()
+ }
+}
+
+/// Prototype for a command that must be executed.
+#[deriving(Clone)]
+pub struct CommandPrototype {
+ ty: CommandType,
+ args: Vec<CString>,
+ env: HashMap<String, Option<CString>>,
+ cwd: Path,
+}
+
+impl CommandPrototype {
+ pub fn new(ty: CommandType) -> CargoResult<CommandPrototype> {
+ use std::os;
+
+ Ok(CommandPrototype {
+ ty: ty,
+ args: Vec::new(),
+ env: HashMap::new(),
+ cwd: try!(os::getcwd()),
+ })
+ }
+
+ pub fn get_type(&self) -> &CommandType {
+ &self.ty
+ }
+
+ pub fn arg<T: ToCStr>(mut self, arg: T) -> CommandPrototype {
+ self.args.push(arg.to_c_str());
+ self
+ }
+
+ pub fn args<T: ToCStr>(mut self, arguments: &[T]) -> CommandPrototype {
+ self.args.extend(arguments.iter().map(|t| t.to_c_str()));
+ self
+ }
+
+ pub fn get_args(&self) -> &[CString] {
+ self.args.as_slice()
+ }
+
+ pub fn cwd(mut self, path: Path) -> CommandPrototype {
+ self.cwd = path;
+ self
+ }
+
+ pub fn get_cwd(&self) -> &Path {
+ &self.cwd
+ }
+
+ pub fn env<T: ToCStr>(mut self, key: &str, val: Option<T>) -> CommandPrototype {
+ self.env.insert(key.to_string(), val.map(|t| t.to_c_str()));
+ self
+ }
+
+ pub fn get_envs(&self) -> &HashMap<String, Option<CString>> {
+ &self.env
+ }
+
+ pub fn into_process_builder(self) -> CargoResult<ProcessBuilder> {
+ let mut builder = try!(match self.ty {
+ CommandType::Rustc => util::process("rustc"),
+ CommandType::Rustdoc => util::process("rustdoc"),
+ CommandType::Target(ref cmd) | CommandType::Host(ref cmd) => {
+ util::process(cmd.as_bytes_no_nul())
+ },
+ });
+
+ for arg in self.args.into_iter() {
+ builder = builder.arg(arg.as_bytes_no_nul());
+ }
+
+ for (key, val) in self.env.into_iter() {
+ builder = builder.env(key.as_slice(), val.as_ref().map(|v| v.as_bytes_no_nul()));
+ }
+
+ builder = builder.cwd(self.cwd);
+
+ Ok(builder)
+ }
+}
+
+impl Show for CommandPrototype {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self.ty {
+ CommandType::Rustc => try!(write!(f, "`rustc")),
+ CommandType::Rustdoc => try!(write!(f, "`rustdoc")),
+ CommandType::Target(ref cmd) | CommandType::Host(ref cmd) => {
+ let cmd = String::from_utf8_lossy(cmd.as_bytes_no_nul());
+ try!(write!(f, "`{}", cmd));
+ },
+ }
+
+ for arg in self.args.iter() {
+ try!(write!(f, " {}", String::from_utf8_lossy(arg.as_bytes_no_nul())));
+ }
+
+ write!(f, "`")
+ }
+}
+
+#[deriving(Clone, Show)]
+pub enum CommandType {
+ Rustc,
+ Rustdoc,
+
+ /// The command is to be executed for the target architecture.
+ Target(CString),
+
+ /// The command is to be executed for the host architecture.
+ Host(CString),
+}
use std::dynamic_lib::DynamicLibrary;
use std::io::{fs, USER_RWX};
use std::io::fs::PathExtensions;
+use std::sync::Arc;
use core::{SourceMap, Package, PackageId, PackageSet, Target, Resolve};
-use util::{mod, CargoResult, ProcessBuilder, human, caused_human};
+use util::{mod, CargoResult, human, caused_human};
use util::{Config, internal, ChainError, Fresh, profile, join_paths, Human};
use self::job::{Job, Work};
pub use self::compilation::Compilation;
pub use self::context::Context;
pub use self::context::Platform;
+pub use self::engine::{CommandPrototype, CommandType, ExecEngine, ProcessEngine};
pub use self::layout::{Layout, LayoutProxy};
pub use self::custom_build::BuildOutput;
mod context;
mod compilation;
mod custom_build;
+mod engine;
mod fingerprint;
mod job;
mod job_queue;
deps: &PackageSet, resolve: &'a Resolve,
sources: &'a SourceMap,
config: &'a Config<'a>,
- build_config: BuildConfig)
+ build_config: BuildConfig,
+ exec_engine: Option<Arc<Box<ExecEngine>>>)
-> CargoResult<Compilation> {
if targets.is_empty() {
return Ok(Compilation::new(pkg))
let mut cx = try!(Context::new(env, resolve, sources, deps, config,
host_layout, target_layout, pkg,
build_config));
+ if let Some(exec_engine) = exec_engine {
+ cx.exec_engine = exec_engine.clone();
+ }
+
let mut queue = JobQueue::new(cx.resolve, deps, cx.config);
// First ensure that the destination directory exists
// may be building a C lib for a plugin
let layout = cx.layout(pkg, Kind::Target);
let output = layout.native(pkg);
- let mut p = try!(process(cmd.next().unwrap(), pkg, target, cx))
+ let mut p = try!(process(CommandType::Host(cmd.next().unwrap().to_c_str()), pkg,
+ target, cx))
.env("OUT_DIR", Some(&output))
.env("DEPS_DIR", Some(&output))
.env("TARGET", Some(cx.target_triple()))
}
let pkg = pkg.to_string();
+ let exec_engine = cx.exec_engine.clone();
+
Ok(Work::new(move |desc_tx: Sender<String>| {
desc_tx.send_opt(p.to_string()).ok();
if first && !output.exists() {
internal("failed to create output directory for build command")
}))
}
- try!(p.exec_with_output().map(|_| ()).map_err(|mut e| {
+ try!(exec_engine.exec_with_output(p).map(|_| ()).map_err(|mut e| {
e.desc = format!("Failed to run custom build command for `{}`\n{}",
pkg, e.desc);
Human(e)
let show_warnings = package.get_package_id() == cx.resolve.root() ||
is_path_source;
let rustc = if show_warnings {rustc} else {rustc.arg("-Awarnings")};
+ let exec_engine = cx.exec_engine.clone();
let filenames = try!(cx.target_filenames(target));
let root = cx.out_dir(package, kind, target);
}
desc_tx.send_opt(rustc.to_string()).ok();
- try!(rustc.exec().chain_error(|| {
+ try!(exec_engine.exec(rustc).chain_error(|| {
human(format!("Could not compile `{}`.", name))
}));
fn prepare_rustc(package: &Package, target: &Target, crate_types: Vec<&str>,
cx: &Context, req: Platform)
- -> CargoResult<Vec<(ProcessBuilder, Kind)>> {
- let base = try!(process("rustc", package, target, cx));
+ -> CargoResult<Vec<(CommandPrototype, Kind)>> {
+ let base = try!(process(CommandType::Rustc, package, target, cx));
let base = build_base_args(cx, base, package, target, crate_types.as_slice());
let target_cmd = build_plugin_args(base.clone(), cx, package, target, Kind::Target);
let kind = Kind::Target;
let pkg_root = package.get_root();
let cx_root = cx.layout(package, kind).proxy().dest().join("doc");
- let rustdoc = try!(process("rustdoc", package, target, cx)).cwd(pkg_root.clone());
+ let rustdoc = try!(process(CommandType::Rustdoc, package, target, cx))
+ .cwd(pkg_root.clone());
let mut rustdoc = rustdoc.arg(target.get_src_path())
.arg("-o").arg(cx_root)
.arg("--crate-name").arg(target.get_name());
let primary = package.get_package_id() == cx.resolve.root();
let name = package.get_name().to_string();
let desc = rustdoc.to_string();
+ let exec_engine = cx.exec_engine.clone();
+
Ok(Work::new(move |desc_tx: Sender<String>| {
desc_tx.send(desc);
if primary {
- try!(rustdoc.exec().chain_error(|| {
+ try!(exec_engine.exec(rustdoc).chain_error(|| {
human(format!("Could not document `{}`.", name))
}))
} else {
- try!(rustdoc.exec_with_output().and(Ok(())).map_err(|err| {
+ try!(exec_engine.exec_with_output(rustdoc).and(Ok(())).map_err(|err| {
match err.exit {
Some(..) => {
caused_human(format!("Could not document `{}`.",
}
fn build_base_args(cx: &Context,
- mut cmd: ProcessBuilder,
+ mut cmd: CommandPrototype,
pkg: &Package,
target: &Target,
- crate_types: &[&str]) -> ProcessBuilder {
+ crate_types: &[&str]) -> CommandPrototype {
let metadata = target.get_metadata();
// TODO: Handle errors in converting paths into args
}
-fn build_plugin_args(mut cmd: ProcessBuilder, cx: &Context, pkg: &Package,
- target: &Target, kind: Kind) -> ProcessBuilder {
+fn build_plugin_args(mut cmd: CommandPrototype, cx: &Context, pkg: &Package,
+ target: &Target, kind: Kind) -> CommandPrototype {
cmd = cmd.arg("--out-dir");
cmd = cmd.arg(cx.out_dir(pkg, kind, target));
cmd = cmd.arg("--emit=dep-info,link");
if kind == Kind::Target {
- fn opt(cmd: ProcessBuilder, key: &str, prefix: &str,
- val: Option<&str>) -> ProcessBuilder {
+ fn opt(cmd: CommandPrototype, key: &str, prefix: &str,
+ val: Option<&str>) -> CommandPrototype {
match val {
Some(val) => {
cmd.arg(key)
return cmd;
}
-fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
+fn build_deps_args(mut cmd: CommandPrototype, target: &Target, package: &Package,
cx: &Context,
- kind: Kind) -> CargoResult<ProcessBuilder> {
+ kind: Kind) -> CargoResult<CommandPrototype> {
let layout = cx.layout(package, kind);
cmd = cmd.arg("-L").arg(layout.root());
cmd = cmd.arg("-L").arg(layout.deps());
return Ok(cmd);
- fn link_to(mut cmd: ProcessBuilder, pkg: &Package, target: &Target,
- cx: &Context, kind: Kind) -> CargoResult<ProcessBuilder> {
+ fn link_to(mut cmd: CommandPrototype, pkg: &Package, target: &Target,
+ cx: &Context, kind: Kind) -> CargoResult<CommandPrototype> {
// If this target is itself a plugin *or* if it's being linked to a
// plugin, then we want the plugin directory. Otherwise we want the
// target directory (hence the || here).
}
}
-pub fn process<T: ToCStr>(cmd: T, pkg: &Package, target: &Target,
- cx: &Context) -> CargoResult<ProcessBuilder> {
+pub fn process(cmd: CommandType, pkg: &Package, target: &Target,
+ cx: &Context) -> CargoResult<CommandPrototype> {
// When invoking a tool, we need the *host* deps directory in the dynamic
// library search path for plugins and such which have dynamic dependencies.
let layout = cx.layout(pkg, Kind::Host);
use core::Source;
use sources::PathSource;
-use ops;
+use ops::{mod, ExecEngine, ProcessEngine};
use util::{CargoResult, ProcessError};
pub struct TestOptions<'a> {
Some(path) => path,
None => exe.clone(),
};
- let cmd = try!(compile.process(exe, &compile.package)).args(test_args);
+ let cmd = try!(compile.target_process(exe, &compile.package))
+ .args(test_args);
try!(options.compile_opts.shell.concise(|shell| {
shell.status("Running", to_display.display().to_string())
}));
try!(options.compile_opts.shell.verbose(|shell| {
shell.status("Running", cmd.to_string())
}));
- match cmd.exec() {
+ match ExecEngine::exec(&mut ProcessEngine, cmd) {
Ok(()) => {}
Err(e) => return Ok(Some(e))
}
for (lib, name) in libs {
try!(options.compile_opts.shell.status("Doc-tests", name));
- let mut p = try!(compile.process("rustdoc", &compile.package))
+ let mut p = try!(compile.rustdoc_process(&compile.package))
.arg("--test").arg(lib)
.arg("--crate-name").arg(name)
.arg("-L").arg(&compile.root_output)
try!(options.compile_opts.shell.verbose(|shell| {
shell.status("Running", p.to_string())
}));
- match p.exec() {
+ match ExecEngine::exec(&mut ProcessEngine, p) {
Ok(()) => {}
Err(e) => return Ok(Some(e)),
}
pub use self::cargo_rustc::{Context, LayoutProxy};
pub use self::cargo_rustc::Platform;
pub use self::cargo_rustc::{BuildOutput, BuildConfig, TargetConfig};
+pub use self::cargo_rustc::{CommandType, CommandPrototype, ExecEngine, ProcessEngine};
pub use self::cargo_run::run;
pub use self::cargo_new::{new, NewOptions};
pub use self::cargo_doc::{doc, DocOptions};